<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Awesome Linux Software</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <script
      crossorigin
      src="https://unpkg.com/react@18/umd/react.production.min.js"
    ></script>
    <script
      crossorigin
      src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"
    ></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style>
      .line-clamp-2 {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
      }
      .search-icon {
        width: 20px;
        height: 20px;
        display: inline-block;
        position: relative;
      }
      .search-icon:before {
        content: "";
        position: absolute;
        top: 2px;
        left: 2px;
        width: 10px;
        height: 10px;
        border: 2px solid currentColor;
        border-radius: 50%;
      }
      .search-icon:after {
        content: "";
        position: absolute;
        top: 11px;
        left: 14px;
        width: 2px;
        height: 7px;
        background: currentColor;
        transform: rotate(45deg);
      }
      .ai-assistant {
        transition: all 0.3s ease;
      }
      .spinner {
        border: 3px solid rgba(255, 255, 255, 0.3);
        border-radius: 50%;
        border-top: 3px solid #fff;
        width: 24px;
        height: 24px;
        animation: spin 1s linear infinite;
      }
      @keyframes spin {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(360deg);
        }
      }
      .modal {
        transition: opacity 0.3s ease;
      }
      .modal-content {
        transition: transform 0.3s ease;
      }
      .modal.active .modal-content {
        transform: translateY(0);
      }
      .modal:not(.active) .modal-content {
        transform: translateY(20px);
      }
    </style>
  </head>
  <body>
    <div id="root"></div>

    <script type="text/babel">
      const { useState, useEffect, useRef } = React;

      const App = () => {
        const [categories, setCategories] = useState([]);
        const [applications, setApplications] = useState([]);
        const [filteredApps, setFilteredApps] = useState([]);
        const [selectedCategory, setSelectedCategory] = useState("all");
        const [searchTerm, setSearchTerm] = useState("");
        const [isLoading, setIsLoading] = useState(true);
        const [activeApp, setActiveApp] = useState(null);
        const [fetchError, setFetchError] = useState(null);
        const [isDarkMode, setIsDarkMode] = useState(
          window.matchMedia &&
            window.matchMedia("(prefers-color-scheme: dark)").matches
        );

        const [isAiOpen, setIsAiOpen] = useState(false);
        const [userQuery, setUserQuery] = useState("");
        const [aiResponse, setAiResponse] = useState("");
        const [aiRecommendations, setAiRecommendations] = useState([]);
        const [isAiLoading, setIsAiLoading] = useState(false);
        const [openAiKey, setOpenAiKey] = useState(
          localStorage.getItem("openAiKey") || ""
        );
        const [showKeyInput, setShowKeyInput] = useState(false);
        const [aiError, setAiError] = useState(null);

        const [showAboutModal, setShowAboutModal] = useState(false);
        const [authorInfo, setAuthorInfo] = useState(null);
        const [isAuthorLoading, setIsAuthorLoading] = useState(false);

        const aiInputRef = useRef(null);
        const messagesEndRef = useRef(null);

        useEffect(() => {
          const fetchData = async () => {
            setIsLoading(true);
            setFetchError(null);

            try {
              const response = await fetch(
                "https://raw.githubusercontent.com/luong-komorebi/Awesome-Linux-Software/master/README.md"
              );

              if (!response.ok) {
                throw new Error(
                  `Failed to fetch data: ${response.status} ${response.statusText}`
                );
              }

              const markdownContent = await response.text();
              const { parsedCategories, parsedApplications } =
                parseMarkdown(markdownContent);

              const allCategoriesData = [
                { id: "all", name: "All Applications" },
                ...parsedCategories,
              ];

              setCategories(allCategoriesData);
              setApplications(parsedApplications);
              setFilteredApps(parsedApplications);
            } catch (error) {
              console.error("Error fetching or parsing data:", error);
              setFetchError(error.message);
              setCategories([{ id: "all", name: "All Applications" }]);
              setApplications([]);
              setFilteredApps([]);
            } finally {
              setIsLoading(false);
            }
          };

          fetchData();
        }, []);

        useEffect(() => {
          if (showAboutModal && !authorInfo && !isAuthorLoading) {
            fetchAuthorInfo();
          }
        }, [showAboutModal]);

        useEffect(() => {
          if (messagesEndRef.current) {
            messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
          }
        }, [aiResponse]);

        useEffect(() => {
          filterApplications();
        }, [selectedCategory, searchTerm, applications]);

        const fetchAuthorInfo = async () => {
          setIsAuthorLoading(true);
          try {
            const response = await fetch(
              "https://api.github.com/users/luong-komorebi"
            );
            if (!response.ok) {
              throw new Error("Failed to fetch author info");
            }
            const data = await response.json();
            setAuthorInfo(data);
          } catch (error) {
            console.error("Error fetching author info:", error);
          } finally {
            setIsAuthorLoading(false);
          }
        };

        const scrollToBottom = () => {
          if (messagesEndRef.current) {
            messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
          }
        };

        const addReferral = (url) => {
          if (!url) return url;

          try {
            const originalUrl = new URL(url);

            // Don't add referral to GitHub links about this project
            if (
              originalUrl.hostname === "github.com" &&
              originalUrl.pathname.includes(
                "luong-komorebi/Awesome-Linux-Software"
              )
            ) {
              return url;
            }

            // Add referrer parameter
            originalUrl.searchParams.append("ref", "awesome-linux-software");

            return originalUrl.toString();
          } catch (e) {
            // If URL parsing fails, return the original URL
            return url;
          }
        };

        const parseMarkdown = (content) => {
          const parsedCategories = [];
          const parsedApplications = [];
          let appId = 1;

          const lines = content.split("\n");
          let currentCategory = null;
          let currentCategoryId = null;
          let currentCategoryName = null;

          const categoryRegex = /^#+\s+(.+)$/;
          const appRegex = /\[([^\]]+)\]\(([^)]+)\)\s*-\s*(.+)$/;

          const mainSections = [
            "Applications",
            "3D Printing",
            "Audio",
            "Chat Clients",
            "Data Backup and Recovery",
            "Desktop Customization",
            "Development",
            "E-Book Utilities",
            "Electronic",
            "Education",
            "Email",
            "File Manager",
            "Games",
            "Graphics",
            "Internet",
            "Office",
            "Productivity",
            "Proxy",
            "Security",
            "Sharing Files",
            "Terminal",
            "Text Editors",
            "Utilities",
            "Video",
            "VPN",
            "Wiki Software",
            "Others",
          ];

          const cleanCategoryId = (name) => {
            return name
              .toLowerCase()
              .replace(/[^\w\s-]/g, "")
              .replace(/\s+/g, "")
              .trim();
          };

          for (let i = 0; i < lines.length; i++) {
            const line = lines[i].trim();

            const categoryMatch = line.match(categoryRegex);
            if (categoryMatch) {
              const potentialCategory = categoryMatch[1];

              if (mainSections.includes(potentialCategory)) {
                currentCategoryName = potentialCategory;
                currentCategoryId = cleanCategoryId(potentialCategory);

                if (potentialCategory !== "Applications") {
                  parsedCategories.push({
                    id: currentCategoryId,
                    name: currentCategoryName,
                  });
                }
              } else if (
                currentCategoryName &&
                mainSections.includes(currentCategoryName)
              ) {
                currentCategory = potentialCategory;
              }
            }

            const appMatch = line.match(appRegex);
            if (
              appMatch &&
              currentCategoryId &&
              currentCategoryId !== "applications"
            ) {
              const name = appMatch[1];
              const website = appMatch[2];
              const description = appMatch[3];

              const isOpenSource = !line.includes("nonfree");

              const generateTags = (name, desc, category) => {
                const tags = new Set();

                tags.add(category.toLowerCase());

                if (currentCategory) {
                  tags.add(currentCategory.toLowerCase());
                }

                const commonWords = [
                  "a",
                  "an",
                  "the",
                  "is",
                  "and",
                  "or",
                  "for",
                  "to",
                  "with",
                  "that",
                  "this",
                ];

                const addWordsFromText = (text) => {
                  const words = text
                    .toLowerCase()
                    .replace(/[^\w\s]/g, "")
                    .split(" ")
                    .filter(
                      (word) => word.length > 3 && !commonWords.includes(word)
                    );

                  words.forEach((word) => tags.add(word));
                };

                addWordsFromText(name);

                const shortDesc = desc.split(" ").slice(0, 8).join(" ");
                addWordsFromText(shortDesc);

                return Array.from(tags).slice(0, 6);
              };

              const app = {
                id: appId++,
                name: name,
                category: currentCategoryId,
                subcategory: currentCategory || null,
                description: description,
                website: website,
                isOpenSource: isOpenSource,
                tags: generateTags(name, description, currentCategoryName),
              };

              parsedApplications.push(app);
            }
          }

          return { parsedCategories, parsedApplications };
        };

        const filterApplications = () => {
          let filtered = [...applications];

          if (selectedCategory !== "all") {
            filtered = filtered.filter(
              (app) => app.category === selectedCategory
            );
          }

          if (searchTerm.trim() !== "") {
            const search = searchTerm.toLowerCase();
            filtered = filtered.filter(
              (app) =>
                app.name.toLowerCase().includes(search) ||
                app.description.toLowerCase().includes(search) ||
                (app.tags && app.tags.some((tag) => tag.includes(search)))
            );
          }

          setFilteredApps(filtered);
        };

        const handleKeyDown = (e) => {
          if (e.key === "Enter" && !e.shiftKey) {
            e.preventDefault();
            handleAiQuery();
          }
        };

        const handleAiQuery = async () => {
          if (!userQuery.trim()) return;

          setIsAiLoading(true);
          setAiError(null);

          const previousQuery = userQuery;
          setUserQuery("");

          try {
            if (!openAiKey) {
              await fallbackRecommendation(previousQuery);
              return;
            }

            const response = await fetch(
              "https://api.openai.com/v1/chat/completions",
              {
                method: "POST",
                headers: {
                  "Content-Type": "application/json",
                  Authorization: `Bearer ${openAiKey}`,
                },
                body: JSON.stringify({
                  model: "gpt-3.5-turbo",
                  messages: [
                    {
                      role: "system",
                      content: `You are a Linux software recommendation assistant. Provide helpful, specific recommendations based on user queries.
                  
Instructions:
1. Recommend 3-5 Linux applications that best match the user's needs
2. Format MUST be consistent:
   - Start with a brief introduction
   - List each recommendation with "• [App Name] - " prefix followed by a brief description
   - Separate introduction from list with a line break

Example format:
"Based on your needs, here are some great Linux applications:

• GIMP - Professional image editor with extensive feature set
• Inkscape - Vector graphics editor perfect for illustrations  
• Krita - Digital painting program with natural media simulation"

Important: Only recommend software that actually exists for Linux. Keep descriptions brief and focused.`,
                    },
                    {
                      role: "user",
                      content: `Recommend Linux software for: "${previousQuery}"`,
                    },
                  ],
                  max_tokens: 500,
                }),
              }
            );

            if (!response.ok) {
              throw new Error(`API Error: ${response.status}`);
            }

            const data = await response.json();
            let aiContent = data.choices[0].message.content;

            // Extract recommendations from the AI response
            const recommendationInfo =
              extractRecommendationsFromAiResponse(aiContent);
            const matchedApps = recommendationInfo.matchedApps;

            // If we have matched apps, ensure the text response only includes apps we can actually show
            if (matchedApps.length > 0) {
              // Split response into introduction and list parts
              const responseLines = aiContent.split("\n");
              const introLines = [];
              let reachedList = false;

              for (const line of responseLines) {
                if (
                  !reachedList &&
                  (line.trim() === "" || line.trim().match(/^[•*\-→▸►]/))
                ) {
                  reachedList = true;
                }

                if (!reachedList) {
                  introLines.push(line);
                }
              }

              // Create new response with matched apps only
              let newResponse = introLines.join("\n");
              if (introLines.length > 0 && !newResponse.endsWith("\n")) {
                newResponse += "\n";
              }
              newResponse += "\n";

              // Add our actual matched apps to the response
              matchedApps.forEach((app) => {
                // Get first sentence of description or truncate if too long
                let shortDesc = app.description.split(".")[0];
                if (shortDesc.length > 100) {
                  shortDesc = shortDesc.substring(0, 97) + "...";
                }
                newResponse += `• ${app.name} - ${shortDesc}.\n`;
              });

              aiContent = newResponse;
            }

            setAiResponse(aiContent);
            setAiRecommendations(matchedApps);
          } catch (error) {
            console.error("AI error:", error);
            setAiError(
              `Error: ${error.message}. ${
                !openAiKey ? " Please add your OpenAI API key." : ""
              }`
            );
            await fallbackRecommendation(previousQuery);
          } finally {
            setIsAiLoading(false);
            setTimeout(scrollToBottom, 100);
          }
        };

        const fallbackRecommendation = async (query) => {
          const queryTerms = query
            .toLowerCase()
            .split(" ")
            .filter((word) => word.length > 3);

          let matchedApps = applications.filter((app) => {
            return queryTerms.some(
              (term) =>
                app.name.toLowerCase().includes(term) ||
                app.description.toLowerCase().includes(term) ||
                app.tags.some((tag) => tag.includes(term))
            );
          });

          if (matchedApps.length === 0) {
            const categoryMatches = categories
              .filter((cat) => cat.id !== "all")
              .filter((cat) =>
                queryTerms.some((term) => cat.name.toLowerCase().includes(term))
              );

            if (categoryMatches.length > 0) {
              const categoryIds = categoryMatches.map((cat) => cat.id);
              matchedApps = applications.filter((app) =>
                categoryIds.includes(app.category)
              );
            }
          }

          // Sort by relevance (number of matching terms)
          matchedApps = matchedApps
            .sort((a, b) => {
              const aMatches = queryTerms.filter(
                (term) =>
                  a.name.toLowerCase().includes(term) ||
                  a.description.toLowerCase().includes(term) ||
                  a.tags.some((tag) => tag.includes(term))
              ).length;

              const bMatches = queryTerms.filter(
                (term) =>
                  b.name.toLowerCase().includes(term) ||
                  b.description.toLowerCase().includes(term) ||
                  b.tags.some((tag) => tag.includes(term))
              ).length;

              return bMatches - aMatches;
            })
            .slice(0, 5);

          if (matchedApps.length > 0) {
            // Create a more structured response similar to the AI output
            let response = `Based on your search for "${query}", here are some Linux applications that might help:\n\n`;

            matchedApps.forEach((app) => {
              let shortDesc = app.description.split(".")[0];
              if (shortDesc.length > 100) {
                shortDesc = shortDesc.substring(0, 97) + "...";
              }
              response += `• ${app.name} - ${shortDesc}.\n`;
            });

            setAiResponse(response);
            setAiRecommendations(matchedApps);
          } else {
            setAiResponse(
              `I couldn't find specific applications matching "${query}". Try browsing by category or using more specific terms.`
            );
            setAiRecommendations([]);
          }
        };

        const extractRecommendationsFromAiResponse = (response) => {
          const appNames = [];
          const appDescriptions = {};
          // Split by lines and look for the bullet point format
          const lines = response.split("\n");

          for (const line of lines) {
            // Match lines that start with a bullet point followed by app name
            // This accommodates various bullet styles: •, *, -, etc.
            const bulletMatch = line.match(
              /^[•*\-→▸►]\s+([^-\–\—]+)[\-\–\—](.+)/
            );
            if (bulletMatch) {
              const namePart = bulletMatch[1].trim();
              const description = bulletMatch[2].trim();
              // If name contains description after a colon, just take the part before the colon
              const name = namePart.includes(":")
                ? namePart.split(":")[0].trim()
                : namePart;
              appNames.push(name);
              appDescriptions[name] = description;
            }
          }

          // If no bullet points found, try the older format with names followed by colons
          if (appNames.length === 0) {
            for (const line of lines) {
              const nameMatch = line.match(/\*\*([^*:]+)\*\*|^([^:]+):/);
              if (nameMatch) {
                const name = (nameMatch[1] || nameMatch[2]).trim();
                appNames.push(name);
              }
            }
          }

          // Map names to actual applications with fuzzy matching
          const matchedApps = appNames
            .map((name) => {
              // First try exact match
              let matchedApps = applications.filter(
                (app) => app.name.toLowerCase() === name.toLowerCase()
              );

              // If no exact matches, try partial matches
              if (matchedApps.length === 0) {
                matchedApps = applications.filter(
                  (app) =>
                    app.name.toLowerCase().includes(name.toLowerCase()) ||
                    name.toLowerCase().includes(app.name.toLowerCase())
                );
              }

              // If still no matches, try matching words
              if (matchedApps.length === 0) {
                const nameWords = name.toLowerCase().split(/\s+/);
                matchedApps = applications.filter((app) =>
                  nameWords.some(
                    (word) =>
                      word.length > 3 && app.name.toLowerCase().includes(word)
                  )
                );
              }

              return matchedApps[0] || null;
            })
            .filter((app) => app !== null);

          return { matchedApps, appNames, appDescriptions };
        };

        const handleCategoryChange = (categoryId) => {
          setSelectedCategory(categoryId);
          setActiveApp(null);
        };

        const handleSearchChange = (e) => {
          setSearchTerm(e.target.value);
          setActiveApp(null);
        };

        const toggleDarkMode = () => {
          setIsDarkMode(!isDarkMode);
        };

        const openAppDetail = (app) => {
          setActiveApp(app);
        };

        const closeAppDetail = () => {
          setActiveApp(null);
        };

        const toggleAiAssistant = () => {
          setIsAiOpen(!isAiOpen);
          if (!isAiOpen) {
            setTimeout(() => {
              if (aiInputRef.current) {
                aiInputRef.current.focus();
              }
            }, 300);
          }
        };

        const saveApiKey = () => {
          localStorage.setItem("openAiKey", openAiKey);
          setShowKeyInput(false);
        };

        const toggleAboutModal = () => {
          setShowAboutModal(!showAboutModal);
        };

        return (
          <div
            className={`min-h-screen flex flex-col ${
              isDarkMode
                ? "bg-gray-900 text-gray-200"
                : "bg-gray-100 text-gray-800"
            }`}
          >
            <header
              className={`py-4 ${isDarkMode ? "bg-gray-800" : "bg-blue-600"}`}
            >
              {/* Privacy Notice Banner */}
              <div
                className={`w-full mb-2 py-1 px-4 text-center text-sm ${
                  isDarkMode ? "bg-gray-700" : "bg-blue-700"
                }`}
              >
                <p className="text-white">
                  🔒 This site runs entirely on your browser - no server, no
                  tracking, no data storage. Your privacy is respected.
                </p>
              </div>

              <div className="container mx-auto px-4 flex justify-between items-center">
                <div className="flex items-center">
                  <span className="text-2xl font-bold text-white mr-2">🐧</span>
                  <h1 className="text-2xl font-bold text-white">
                    Awesome Linux Software
                  </h1>
                </div>
                <div className="flex space-x-2">
                  <button
                    onClick={toggleAboutModal}
                    className={`px-3 py-1 rounded-md ${
                      isDarkMode
                        ? "bg-gray-700 hover:bg-gray-600"
                        : "bg-blue-500 hover:bg-blue-400"
                    } text-white`}
                  >
                    About
                  </button>
                  <button
                    onClick={toggleAiAssistant}
                    className={`px-3 py-1 rounded-md ${
                      isDarkMode
                        ? "bg-gray-700 hover:bg-gray-600"
                        : "bg-blue-500 hover:bg-blue-400"
                    } text-white flex items-center`}
                  >
                    <span className="mr-1">🤖</span> AI Software Finder
                  </button>
                  <button
                    onClick={toggleDarkMode}
                    className={`px-3 py-1 rounded-md ${
                      isDarkMode
                        ? "bg-gray-700 text-yellow-300"
                        : "bg-blue-500 text-white"
                    }`}
                  >
                    {isDarkMode ? "☀️ Light" : "🌙 Dark"}
                  </button>
                </div>
              </div>
            </header>

            <div className="container mx-auto px-4 py-6 flex-grow">
              <div className="mb-6">
                <div
                  className={`flex items-center p-2 rounded-lg ${
                    isDarkMode ? "bg-gray-800" : "bg-white"
                  } shadow`}
                >
                  <span
                    className={`search-icon mr-2 ${
                      isDarkMode ? "text-gray-400" : "text-gray-500"
                    }`}
                  ></span>
                  <input
                    type="text"
                    placeholder="Search applications, categories, or tags..."
                    className={`w-full p-2 outline-none ${
                      isDarkMode
                        ? "bg-gray-800 text-white"
                        : "bg-white text-gray-800"
                    }`}
                    value={searchTerm}
                    onChange={handleSearchChange}
                  />
                </div>
              </div>

              <div className="mb-6 overflow-x-auto">
                <div className="flex space-x-2 pb-2">
                  {categories.map((category) => (
                    <button
                      key={category.id}
                      onClick={() => handleCategoryChange(category.id)}
                      className={`px-4 py-2 rounded-full whitespace-nowrap ${
                        selectedCategory === category.id
                          ? isDarkMode
                            ? "bg-blue-600 text-white"
                            : "bg-blue-600 text-white"
                          : isDarkMode
                          ? "bg-gray-800 text-gray-300 hover:bg-gray-700"
                          : "bg-gray-200 text-gray-700 hover:bg-gray-300"
                      }`}
                    >
                      {category.name}
                    </button>
                  ))}
                </div>
              </div>

              {isLoading ? (
                <div className="flex flex-col justify-center items-center h-64">
                  <div
                    className={`animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 ${
                      isDarkMode ? "border-blue-500" : "border-blue-600"
                    } mb-4`}
                  ></div>
                  <p>Loading applications from GitHub...</p>
                </div>
              ) : fetchError ? (
                <div
                  className={`rounded-lg shadow p-6 ${
                    isDarkMode ? "bg-red-900" : "bg-red-100"
                  } mb-6`}
                >
                  <h2 className="text-xl font-bold mb-2">Error Loading Data</h2>
                  <p>{fetchError}</p>
                  <p className="mt-2">
                    Please try again later or check your connection.
                  </p>
                </div>
              ) : (
                <>
                  {activeApp ? (
                    <div
                      className={`rounded-lg shadow-lg p-6 ${
                        isDarkMode ? "bg-gray-800" : "bg-white"
                      }`}
                    >
                      <div className="flex justify-between items-center mb-4">
                        <h2 className="text-2xl font-bold">{activeApp.name}</h2>
                        <button
                          onClick={closeAppDetail}
                          className={`px-3 py-1 rounded-md ${
                            isDarkMode
                              ? "bg-gray-700 hover:bg-gray-600"
                              : "bg-gray-200 hover:bg-gray-300"
                          }`}
                        >
                          Back
                        </button>
                      </div>

                      <div className="mb-4">
                        <span
                          className={`inline-block px-3 py-1 rounded-full text-sm mr-2 ${
                            isDarkMode
                              ? "bg-blue-900 text-blue-300"
                              : "bg-blue-100 text-blue-800"
                          }`}
                        >
                          {categories.find((c) => c.id === activeApp.category)
                            ?.name || activeApp.category}
                        </span>

                        {activeApp.subcategory && (
                          <span
                            className={`inline-block px-3 py-1 rounded-full text-sm mr-2 ${
                              isDarkMode
                                ? "bg-purple-900 text-purple-300"
                                : "bg-purple-100 text-purple-800"
                            }`}
                          >
                            {activeApp.subcategory}
                          </span>
                        )}

                        {activeApp.isOpenSource && (
                          <span
                            className={`inline-block px-3 py-1 rounded-full text-sm ${
                              isDarkMode
                                ? "bg-green-900 text-green-300"
                                : "bg-green-100 text-green-800"
                            }`}
                          >
                            Open Source
                          </span>
                        )}
                      </div>

                      <p className="text-lg mb-4">{activeApp.description}</p>

                      {activeApp.tags && activeApp.tags.length > 0 && (
                        <div className="mb-6">
                          <h3 className="font-semibold mb-2">Tags:</h3>
                          <div className="flex flex-wrap gap-2">
                            {activeApp.tags.map((tag, index) => (
                              <span
                                key={index}
                                className={`px-2 py-1 rounded-md text-sm ${
                                  isDarkMode ? "bg-gray-700" : "bg-gray-200"
                                }`}
                              >
                                {tag}
                              </span>
                            ))}
                          </div>
                        </div>
                      )}

                      <a
                        href={addReferral(activeApp.website)}
                        target="_blank"
                        rel="noopener noreferrer"
                        className={`inline-block px-4 py-2 rounded-md ${
                          isDarkMode
                            ? "bg-blue-600 hover:bg-blue-700"
                            : "bg-blue-600 hover:bg-blue-700"
                        } text-white`}
                      >
                        Visit Website
                      </a>
                    </div>
                  ) : (
                    <>
                      <div className="mb-4">
                        <p
                          className={`${
                            isDarkMode ? "text-gray-400" : "text-gray-600"
                          }`}
                        >
                          Showing {filteredApps.length} applications
                          {selectedCategory !== "all" &&
                            ` in ${
                              categories.find((c) => c.id === selectedCategory)
                                ?.name || selectedCategory
                            }`}
                          {searchTerm && ` matching "${searchTerm}"`}
                        </p>
                      </div>

                      {filteredApps.length === 0 && (
                        <div
                          className={`text-center py-12 ${
                            isDarkMode ? "bg-gray-800" : "bg-white"
                          } rounded-lg shadow`}
                        >
                          <p className="text-xl">
                            No applications found matching your criteria
                          </p>
                          <p className="mt-2 text-gray-500">
                            Try adjusting your search or category filter
                          </p>
                        </div>
                      )}

                      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
                        {filteredApps.map((app) => (
                          <div
                            key={app.id}
                            className={`rounded-lg shadow-md overflow-hidden cursor-pointer transition-transform hover:scale-105 ${
                              isDarkMode ? "bg-gray-800" : "bg-white"
                            }`}
                            onClick={() => openAppDetail(app)}
                          >
                            <div className="p-5">
                              <div className="flex justify-between items-start">
                                <h2 className="text-xl font-semibold mb-2">
                                  {app.name}
                                </h2>
                                {app.isOpenSource && (
                                  <span
                                    className={`inline-block px-2 py-1 rounded-full text-xs ${
                                      isDarkMode
                                        ? "bg-green-900 text-green-300"
                                        : "bg-green-100 text-green-800"
                                    }`}
                                  >
                                    Open Source
                                  </span>
                                )}
                              </div>
                              <p
                                className={`mb-4 text-sm ${
                                  isDarkMode ? "text-gray-400" : "text-gray-600"
                                } line-clamp-2`}
                              >
                                {app.description}
                              </p>
                              <div className="flex items-center justify-between">
                                <span
                                  className={`inline-block px-3 py-1 rounded-full text-xs ${
                                    isDarkMode
                                      ? "bg-blue-900 text-blue-300"
                                      : "bg-blue-100 text-blue-800"
                                  }`}
                                >
                                  {categories.find((c) => c.id === app.category)
                                    ?.name || app.category}
                                </span>
                                <span
                                  className={`text-sm ${
                                    isDarkMode
                                      ? "text-blue-400"
                                      : "text-blue-600"
                                  }`}
                                >
                                  View details →
                                </span>
                              </div>
                            </div>
                          </div>
                        ))}
                      </div>
                    </>
                  )}
                </>
              )}
            </div>

            {/* About Modal */}
            <div
              className={`modal fixed inset-0 bg-black bg-opacity-50 z-50 flex justify-center items-center ${
                showAboutModal
                  ? "active opacity-100"
                  : "opacity-0 pointer-events-none"
              }`}
            >
              <div
                className={`modal-content relative bg-white dark:bg-gray-800 w-full max-w-3xl rounded-lg shadow-lg max-h-[90vh] overflow-auto ${
                  isDarkMode ? "text-white" : "text-gray-800"
                }`}
              >
                <div className="sticky top-0 bg-blue-600 dark:bg-gray-700 text-white p-4 flex justify-between items-center">
                  <h2 className="text-xl font-bold">
                    About Awesome Linux Software
                  </h2>
                  <button
                    onClick={toggleAboutModal}
                    className="text-white hover:bg-blue-700 dark:hover:bg-gray-600 p-1 rounded"
                  >
                    ✕
                  </button>
                </div>

                <div className="p-6">
                  <div className="mb-8">
                    <h3 className="text-lg font-semibold mb-4">
                      About This Project
                    </h3>
                    <p className="mb-4">
                      Awesome Linux Software is a web application that helps you
                      discover and explore Linux applications from the{" "}
                      <a
                        href="https://github.com/luong-komorebi/Awesome-Linux-Software"
                        target="_blank"
                        rel="noopener noreferrer"
                        className={`${
                          isDarkMode ? "text-blue-400" : "text-blue-600"
                        } hover:underline`}
                      >
                        Awesome-Linux-Software
                      </a>{" "}
                      GitHub repository.
                    </p>
                    <p>
                      This tool parses the markdown content directly from the
                      repository and presents it in a searchable, filterable
                      interface with detailed information about each
                      application.
                    </p>
                  </div>

                  <div className="mb-8">
                    <h3 className="text-lg font-semibold mb-4">Author</h3>

                    {isAuthorLoading ? (
                      <div className="flex justify-center py-8">
                        <div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500"></div>
                      </div>
                    ) : authorInfo ? (
                      <div className="flex flex-col md:flex-row items-center md:items-start gap-6">
                        <div className="w-32 h-32 rounded-full overflow-hidden">
                          <img
                            src={authorInfo.avatar_url}
                            alt={authorInfo.name || authorInfo.login}
                            className="w-full h-full object-cover"
                          />
                        </div>
                        <div>
                          <h4 className="text-xl font-semibold">
                            {authorInfo.name || authorInfo.login}
                          </h4>
                          {authorInfo.bio && (
                            <p
                              className={`${
                                isDarkMode ? "text-gray-300" : "text-gray-600"
                              } mt-2`}
                            >
                              {authorInfo.bio}
                            </p>
                          )}

                          <div className="mt-4 space-y-2">
                            {authorInfo.location && (
                              <div className="flex items-center">
                                <span className="mr-2">📍</span>
                                <span>{authorInfo.location}</span>
                              </div>
                            )}

                            {authorInfo.blog && (
                              <div className="flex items-center">
                                <span className="mr-2">🔗</span>
                                <a
                                  href={
                                    authorInfo.blog.startsWith("http")
                                      ? authorInfo.blog
                                      : `https://${authorInfo.blog}`
                                  }
                                  target="_blank"
                                  rel="noopener noreferrer"
                                  className={`${
                                    isDarkMode
                                      ? "text-blue-400"
                                      : "text-blue-600"
                                  } hover:underline`}
                                >
                                  {authorInfo.blog}
                                </a>
                              </div>
                            )}

                            <div className="flex items-center">
                              <span className="mr-2">🐙</span>
                              <a
                                href={authorInfo.html_url}
                                target="_blank"
                                rel="noopener noreferrer"
                                className={`${
                                  isDarkMode ? "text-blue-400" : "text-blue-600"
                                } hover:underline`}
                              >
                                GitHub Profile
                              </a>
                            </div>
                          </div>
                        </div>
                      </div>
                    ) : (
                      <p>
                        Could not load author information. Please visit the{" "}
                        <a
                          href="https://github.com/luong-komorebi"
                          target="_blank"
                          rel="noopener noreferrer"
                          className={`${
                            isDarkMode ? "text-blue-400" : "text-blue-600"
                          } hover:underline`}
                        >
                          GitHub profile
                        </a>{" "}
                        for more information.
                      </p>
                    )}
                  </div>

                  <div className="mb-8">
                    <h3 className="text-lg font-semibold mb-4">Contributors</h3>
                    <div className="flex flex-col items-center text-center">
                      <a
                        href="https://github.com/luong-komorebi/Awesome-Linux-Software/graphs/contributors"
                        target="_blank"
                        rel="noopener noreferrer"
                      >
                        <img
                          src="https://contrib.rocks/image?repo=luong-komorebi/Awesome-Linux-Software"
                          alt="Contributors"
                          className="max-w-full rounded-lg"
                        />
                      </a>
                      <p className="mt-3">
                        Made with{" "}
                        <a
                          href="https://contrib.rocks"
                          target="_blank"
                          rel="noopener noreferrer"
                          className={`${
                            isDarkMode ? "text-blue-400" : "text-blue-600"
                          } hover:underline`}
                        >
                          contrib.rocks
                        </a>
                      </p>
                    </div>
                  </div>

                  <div>
                    <h3 className="text-lg font-semibold mb-4">Contribute</h3>
                    <p className="mb-4">
                      The Awesome Linux Software project is open source and
                      welcomes contributions. If you know a great Linux
                      application that isn't included, or want to help improve
                      the list, please consider contributing to the repository.
                    </p>
                    <div className="flex justify-center mt-6">
                      <a
                        href="https://github.com/luong-komorebi/Awesome-Linux-Software"
                        target="_blank"
                        rel="noopener noreferrer"
                        className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-md"
                      >
                        View on GitHub
                      </a>
                    </div>
                  </div>
                </div>
              </div>
            </div>

            {/* AI Assistant */}
            <div
              className={`ai-assistant fixed bottom-0 right-0 z-40 ${
                isAiOpen ? "w-96 h-[450px]" : "w-0 h-0"
              } overflow-hidden transition-all duration-300 ease-in-out ${
                isDarkMode ? "bg-gray-800" : "bg-white"
              } shadow-lg rounded-tl-lg`}
            >
              <div className="flex flex-col h-full">
                <div
                  className={`flex justify-between items-center p-3 ${
                    isDarkMode ? "bg-gray-700" : "bg-blue-600"
                  } text-white`}
                >
                  <div className="flex items-center">
                    <span className="mr-2">🤖</span>
                    <h3 className="font-semibold">AI Software Finder</h3>
                  </div>
                  <div className="flex items-center">
                    {!openAiKey && (
                      <button
                        onClick={() => setShowKeyInput(!showKeyInput)}
                        className="text-sm mr-2 hover:underline"
                      >
                        API Key
                      </button>
                    )}
                    <button
                      onClick={toggleAiAssistant}
                      className="hover:bg-opacity-50 p-1 rounded"
                    >
                      ✕
                    </button>
                  </div>
                </div>

                {showKeyInput && (
                  <div
                    className={`p-3 ${
                      isDarkMode ? "bg-gray-700" : "bg-blue-100"
                    }`}
                  >
                    <p className="text-sm mb-2">
                      Enter your OpenAI API key to enable intelligent software
                      recommendations:
                    </p>
                    <input
                      type="password"
                      value={openAiKey}
                      onChange={(e) => setOpenAiKey(e.target.value)}
                      placeholder="Enter OpenAI API Key"
                      className={`w-full p-2 rounded mb-2 ${
                        isDarkMode ? "bg-gray-800 text-white" : "bg-white"
                      } outline-none`}
                    />
                    <div className="flex justify-between items-center">
                      <button
                        onClick={saveApiKey}
                        className={`text-sm px-3 py-1 rounded ${
                          isDarkMode
                            ? "bg-blue-600 hover:bg-blue-500"
                            : "bg-blue-500 hover:bg-blue-400"
                        } text-white`}
                      >
                        Save Key
                      </button>
                      <a
                        href="https://platform.openai.com/api-keys"
                        target="_blank"
                        rel="noopener noreferrer"
                        className="text-xs underline"
                      >
                        Get an API key
                      </a>
                    </div>
                  </div>
                )}

                <div className="flex-grow overflow-y-auto p-4">
                  <div className="text-sm mb-4">
                    Ask me about Linux software, or describe your needs for app
                    recommendations.
                    {!openAiKey && (
                      <div
                        className={`mt-2 p-2 border ${
                          isDarkMode
                            ? "border-yellow-600 bg-yellow-900 bg-opacity-30"
                            : "border-yellow-500 bg-yellow-100"
                        } rounded-md`}
                      >
                        <p className="font-semibold">⚠️ Limited Mode</p>
                        <p className="mt-1">
                          Without an OpenAI API key, I can only provide basic
                          recommendations based on keyword matching.
                        </p>
                        <button
                          onClick={() => setShowKeyInput(true)}
                          className={`mt-2 text-xs px-2 py-1 rounded ${
                            isDarkMode
                              ? "bg-yellow-700 hover:bg-yellow-600"
                              : "bg-yellow-200 hover:bg-yellow-300"
                          }`}
                        >
                          Add OpenAI API Key
                        </button>
                      </div>
                    )}
                  </div>

                  {aiResponse && (
                    <div
                      className={`p-3 rounded-lg mb-3 ${
                        isDarkMode ? "bg-gray-700" : "bg-blue-100"
                      }`}
                    >
                      <div className="whitespace-pre-line">{aiResponse}</div>

                      {aiRecommendations.length > 0 && (
                        <div className="mt-3">
                          <div className="font-semibold mb-2">
                            {openAiKey
                              ? "AI Recommendations:"
                              : "Keyword Matches:"}
                          </div>
                          <div className="space-y-2">
                            {aiRecommendations.map((app) => (
                              <div
                                key={app.id}
                                onClick={() => openAppDetail(app)}
                                className={`p-2 rounded cursor-pointer ${
                                  isDarkMode
                                    ? "bg-gray-600 hover:bg-gray-500"
                                    : "bg-white hover:bg-gray-100"
                                }`}
                              >
                                <div className="font-medium flex items-center">
                                  {app.name}
                                  {app.isOpenSource && (
                                    <span
                                      className={`ml-2 px-1.5 py-0.5 text-xs rounded ${
                                        isDarkMode
                                          ? "bg-green-900 text-green-300"
                                          : "bg-green-100 text-green-800"
                                      }`}
                                    >
                                      Open Source
                                    </span>
                                  )}
                                </div>
                                <div className="text-xs line-clamp-2">
                                  {app.description}
                                </div>
                                <div className="mt-1 flex justify-between items-center">
                                  <span
                                    className={`inline-block px-2 py-0.5 rounded-full text-xs ${
                                      isDarkMode
                                        ? "bg-blue-900 text-blue-300"
                                        : "bg-blue-100 text-blue-800"
                                    }`}
                                  >
                                    {categories.find(
                                      (c) => c.id === app.category
                                    )?.name || app.category}
                                  </span>
                                  <span className="text-xs underline">
                                    View details
                                  </span>
                                </div>
                              </div>
                            ))}
                          </div>
                        </div>
                      )}
                    </div>
                  )}

                  {aiError && (
                    <div
                      className={`p-3 rounded-lg mb-3 ${
                        isDarkMode ? "bg-red-900" : "bg-red-100"
                      }`}
                    >
                      {aiError}
                    </div>
                  )}

                  <div ref={messagesEndRef} />
                </div>

                <div
                  className={`p-3 ${
                    isDarkMode ? "bg-gray-700" : "bg-gray-100"
                  }`}
                >
                  <div className="relative">
                    <textarea
                      ref={aiInputRef}
                      value={userQuery}
                      onChange={(e) => setUserQuery(e.target.value)}
                      onKeyDown={handleKeyDown}
                      placeholder="Ask about Linux software..."
                      className={`w-full p-2 pr-12 rounded resize-none ${
                        isDarkMode ? "bg-gray-800 text-white" : "bg-white"
                      } outline-none`}
                      rows="2"
                      disabled={isAiLoading}
                    ></textarea>
                    <button
                      onClick={handleAiQuery}
                      disabled={isAiLoading || !userQuery.trim()}
                      className={`absolute right-2 bottom-2 p-2 rounded-full ${
                        isDarkMode
                          ? "bg-blue-600 hover:bg-blue-500"
                          : "bg-blue-500 hover:bg-blue-400"
                      } text-white ${
                        isAiLoading || !userQuery.trim()
                          ? "opacity-50 cursor-not-allowed"
                          : ""
                      }`}
                    >
                      {isAiLoading ? (
                        <div className="spinner"></div>
                      ) : (
                        <span>➤</span>
                      )}
                    </button>
                  </div>
                  {!openAiKey && (
                    <div className="text-xs text-center mt-1 opacity-80">
                      Running in limited mode. Add an OpenAI API key for
                      intelligent recommendations.
                    </div>
                  )}
                </div>
              </div>
            </div>

            <footer
              className={`py-4 ${
                isDarkMode ? "bg-gray-800" : "bg-gray-200"
              } mt-auto`}
            >
              <div className="container mx-auto px-4 text-center">
                <p>
                  Based on{" "}
                  <a
                    href="https://github.com/luong-komorebi/Awesome-Linux-Software"
                    target="_blank"
                    rel="noopener noreferrer"
                    className={`${
                      isDarkMode ? "text-blue-400" : "text-blue-600"
                    } hover:underline`}
                  >
                    Awesome Linux Software
                  </a>{" "}
                  repository
                </p>
                <p className="mt-1 text-sm">
                  Client-side application with AI-powered recommendations
                </p>
              </div>
            </footer>
          </div>
        );
      };

      ReactDOM.createRoot(document.getElementById("root")).render(<App />);
    </script>
  </body>
</html>
